/*
 * Reksio - Memory Map Editor
 * Copyright (C) 2023 CERN
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * In applying this licence, CERN does not waive the privileges and immunities
 * granted to it by virtue of its status as an Intergovernmental Organization or
 * submit itself to any jurisdiction.
 */

#ifndef VALIDATOR_H
#define VALIDATOR_H
#include <string>
#include <memory>
#include <list>
#include <regex>
#include <limits>
#include <yaml-cpp/yaml.h>
struct INodeRuleValidator; // fwd declaration
class MemoryNode;
class Attribute;
class ValidatorNode;

class SchemaValidator
{
public:
    bool validate(const YAML::Node &schema, const YAML::Node &node, const bool strict_validation = false);
    std::vector<std::string> errors;
    static std::vector<std::unique_ptr<INodeRuleValidator>> validatorFactory(const YAML::Node& schema_node, const std::string& node_name);
protected:
    bool _validate(const YAML::Node &schema, const YAML::Node &node, const bool fromSequence = false, const bool strict_validation = false);
    bool validateMapping(const YAML::Node &schema, const YAML::Node &node, const bool fromSequence = false, const bool strict_validation = false);
    bool validateSequence(const YAML::Node &schema, const YAML::Node &node, const bool strict_validation = false);
    bool validateScalar(const YAML::Node &schema, const YAML::Node &node, const bool strict_validation = false);
    void createValidators(const YAML::Node& schema_node);
    std::string getPath(std::vector<std::string>& path);

    void logError(const std::string& err);

    std::vector<std::string> schema_path;
    std::vector<std::string> node_path;
    std::vector<std::string> node_full_name;
    std::map<std::string, std::list<std::unique_ptr<const INodeRuleValidator>>> validators = {};
};

struct INodeRuleValidator
{
    INodeRuleValidator(const std::string node_name): node_name(node_name){}
    virtual bool validate(const YAML::Node& node) const;
    virtual bool validate(const std::string& value) const = 0;
    virtual bool validate(const Attribute* attr) const;
    virtual std::string getMessage() const = 0;
    virtual ~INodeRuleValidator(){}
    const std::string& nodeName() const;
protected:
    const std::string node_name;
};

class NodeRequiredValidator : public INodeRuleValidator
{
public:
    explicit NodeRequiredValidator(const YAML::Node& schema_node, const std::string& node_name);
    virtual bool validate(const YAML::Node& node) const override;

    virtual bool validate(const std::string& value) const override;
    virtual std::string getMessage() const override;
    bool isRequired() const;
protected:
    bool required;
};

class NodeTypeValidator : public INodeRuleValidator
{
public:
    explicit NodeTypeValidator(const YAML::Node& schema_node, const std::string& node_name);
    virtual bool validate(const YAML::Node& node) const override;

    virtual bool validate(const std::string&) const override;

    virtual std::string getMessage() const override;
    const std::string& getTargetType() const;
protected:
    std::string target_type;
};

class NodeEnumValidator : public INodeRuleValidator
{
public:
    explicit NodeEnumValidator(const YAML::Node& schema_node, const std::string& node_name);

    virtual bool validate(const std::string& value) const override;
    virtual std::string getMessage() const override;
    const std::vector<std::string>& getEnums() const;
protected:
    std::vector<std::string> enum_values;
};


class NodeRegexValidator : public INodeRuleValidator
{
public:
    explicit NodeRegexValidator(const YAML::Node& schema_node, const std::string& node_name);

    virtual bool validate(const std::string& value) const override;

    virtual std::string getMessage() const override;
    std::string getRegexStr() const;
protected:
    std::string regex_str;
    std::regex regex;
};

class NodeRangeValidator : public INodeRuleValidator
{
public:
    explicit NodeRangeValidator(const YAML::Node& schema_node, const std::string& node_name);

    virtual bool validate(const std::string& value) const override;

    virtual std::string getMessage() const override;

    bool hasMin() const;

    bool hasMax() const;

    double getMin() const;
    double getMax() const;
protected:
    bool min_ex_en = false;
    bool min_en = false;
    bool max_ex_en = false;
    bool max_en = false;
    double min = 0;
    double max = 0;
};

#endif // VALIDATOR_H
